home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / video / pictetri.src / pictetri / pictetris-src / pictetris.c < prev    next >
C/C++ Source or Header  |  1995-12-22  |  19KB  |  557 lines

  1. /***************************************************************************\
  2. |*                                       *|
  3. |*  pict.c:    A version of Tetris to run on Linux SVGAlib console.       *|
  4. |*        Main module                                            *|
  5. |*                                       *|
  6. |*  Authors:    Mike Taylor (mirk@uk.ac.warwick.cs) &               *|
  7. |*        Arturo Espinosa (arturo@nuclecu.unam.mx)           *|
  8. |*  Started:    Fri May 26 12:26:05 BST 1989 (tetris for terminals)       *|
  9. |*            Dic 1, 1995 (pictetris)                       *|
  10. |*                                       *|
  11. \***************************************************************************/
  12.  
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <jlib.h>
  16. #include <signal.h>
  17. #include <sys/types.h>
  18. #include <fcntl.h>
  19. #include <errno.h>
  20. #include <sys/stat.h>
  21. #include <unistd.h>
  22. #include <pwd.h>
  23. #include <string.h>
  24.  
  25. /* Here goes what's needed to make this puppy work under Linux.  */
  26. #define F_TLOCK    2
  27. #define BADSIG2    -1  /* appended 2 just to avoid the BADSIG redef. warning */
  28. void l_bcopy (const void* s, void* d, int n)
  29. {if (n > 0) memmove (d, s, (size_t) n);}
  30.  
  31. #include "pictetris.h"
  32. #include "utils.h"
  33. #include "screen.h"
  34. #include "game.h"
  35.  
  36. /*-------------------------------------------------------------------------*/
  37.  
  38. extern time_t time ();
  39. extern char *ctime ();
  40. extern char *malloc ();
  41. extern char *getenv ();
  42. extern char *getlogin ();
  43.  
  44. extern int rand_lines;             /* Garbage lines at the start of the game */
  45. extern int next_box;               /* Flag for the next-piece box */
  46. extern int black_bg;               /* Flag for the black background */
  47. extern int sound;                  /* Flag for sound */
  48.  
  49. /*-------------------------------------------------------------------------*/
  50.  
  51. int in_curses = 0;        /* Set to 1 after initialisation */
  52. int rotate_backwards = 0;    /* If set non-zero, rotate clockwise */
  53. int no_hiscores = 0;        /* Number of hi-scores in the table */
  54. int no_shown = NO_SHOWN;    /* Number of hi-scores to list */
  55. int game_level = 0;        /* Number of free pieces */
  56. int rand_lines = 0;             /* Number of random lines (new option) */
  57. int score;            /* Accumulated game score */
  58. int no_pieces;            /* Number of pieces dropped so far */
  59. int no_levels;            /* Number of levels filled & deleted */
  60. int total_time = 50000;        /* Allow 1/4 second before falling: used to be long & 200000*/
  61. int total_time2=0;        /* This one stores the speed level */
  62. int black_bg = 0;               /* You want no picture on the playing area? */
  63. int next_box =1;                /* Couldn't forget a next-piece box */
  64. int sound=1;                    /* Sound flag */
  65. char prog_name[LINELEN];    /* Will be the basename of argv[0] */
  66. char user_name[NAMELEN];    /* From environment: TTNAME or NAME */
  67. char user_code[CODELEN];    /* From getpwuid(getuid())->pw_name */
  68. int board[GAME_DEPTH+4][GAME_WIDTH];
  69. struct score_ent hi_scores[NO_HISCORES];
  70.  
  71. char tc_string[LINELEN];    /* Needed as static storage for the awful */
  72. char *so_str;            /* ... tgetstr() function.  so_str and ... */
  73. char *se_str;            /* ... se_str point into it. */
  74.   
  75. /***************************************************************************\
  76. |*                                       *|
  77. |*  This function is called if a SIGHUP, SIGINT or SIGTERM is caught,       *|
  78. |*  and merely returns the user to standard terminal modes, (ie. exits       *|
  79. |*  from curses(3X) before die()ing.                       *|
  80. |*                                       *|
  81. \***************************************************************************/
  82.  
  83. void signal_end ()
  84. {
  85.   if (in_curses)
  86.     print_msg ("Aborted!");
  87.   
  88.   die (LE_OK, "");
  89. }
  90.  
  91. /***************************************************************************\
  92. |*                                       *|
  93. |*  The function get_scores() reads the contents of the global array       *|
  94. |*  hi_scores from the file named in the #definition of SCORE_FILE.       *|
  95. |*  It also sets no_hiscores to the number of scores in the table.       *|
  96. |*                                       *|
  97. \***************************************************************************/
  98.  
  99. void get_scores ()
  100. {
  101.   int fd;
  102.   struct stat stat_buf;
  103.  
  104.   if ((fd = open (SCORE_FILE, O_RDONLY)) == -1) {
  105.     if (errno != ENOENT)
  106.       die (LE_OPEN, "couldn't open(2) high-score file for reading");
  107.     else {
  108.       no_hiscores = 0;
  109.       return;
  110.     }
  111.   }
  112.  
  113.   if (fstat (fd, &stat_buf) == -1)
  114.     die (LE_STAT, "couldn't stat(2) high-score file");
  115.   no_hiscores = stat_buf.st_size/sizeof (struct score_ent);
  116.  
  117.   if (read (fd, (char*) hi_scores, (int) stat_buf.st_size) == -1) {
  118.     (void) perror ("read()");
  119.       die (LE_READ, "couldn't read(2) high-score file");
  120.   }
  121.  
  122.   (void) close (fd);
  123. }
  124.  
  125. /***************************************************************************\
  126. |*                                       *|
  127. |*  The function print_scores() gets the table in from the disk_file,       *|
  128. |*  and prints it in a nice, human-readable format.               *|
  129. |*                                       *|
  130. \***************************************************************************/
  131.  
  132. void print_scores ()
  133. {
  134.   int i;
  135.   
  136.   get_scores ();
  137.    
  138.   if (no_hiscores == 0)
  139.     print_msg ("There are no high-scores (yet!)");
  140.   else {
  141.     draw_border(0,0,37,20);
  142.     draw_border(38,0,150,20);
  143.     draw_border(151,0,191,20);
  144.     draw_border(192,0,236,20);
  145.     draw_border(237,0,281,20);
  146.     draw_border(282,0,319,20);
  147.     draw_border(0,21,37,199);
  148.     draw_border(38,21,150,199);
  149.     draw_border(151,21,191,199);
  150.     draw_border(192,21,236,199);
  151.     draw_border(237,21,281,199);
  152.     draw_border(282,21,319,199);
  153.  
  154. /* buff_draw_string is a Jlib function */     
  155.      
  156.      buff_draw_string(offscreen,"Rank     Name                         Score      Pieces     Levels     Type"
  157.              ,8,8,1);
  158.      for (i = 0; i < ((no_hiscores < no_shown) ? no_hiscores : no_shown); i++)
  159.       buff_draw_string(offscreen,form("%5d    %-*.*s    %6d    %7d    %7d    %5d",
  160.              i+1, NAMELEN, NAMELEN, hi_scores[i].name,hi_scores[i].score,
  161.              hi_scores[i].no_pieces, hi_scores[i].no_levels,hi_scores[i].game_level),
  162.              8,29+LINE(i*2),1);
  163.   }
  164. }
  165.  
  166. /***************************************************************************\
  167. |*                                       *|
  168. |*  The function update_file puts a lock on the high-score-file, and       *|
  169. |*  then (if successful), reads the high-scores, inserts the current       *|
  170. |*  score in the table if it's good enough, and writes it back if it       *|
  171. |*  has changed, finally removing the lock.  It returns the player's       *|
  172. |*  position in the table, or 0 if he is unplaced.  If we were unable       *|
  173. |*  to get the lock-file, it returns -1.                   *|
  174. |*                                       *|
  175. |*  I'm about to do some mods allowing compilation to be done with       *|
  176. |*  the flag -DLOCKF to do the mutual exclusion using lockf(3) instead       *|
  177. |*  a lock-file.  This will be unneccesary on most systems, but here on       *|
  178. |*  the Warwick systems, some users have titchy quotas that cause them       *|
  179. |*  to be unable to create a lock-file, and thus to use the high-score       *|
  180. |*  table.  I'm using lockf(3) instead of flock(2) since it works across   *|
  181. |*  machines.                                   *|
  182. |*                                       *|
  183. |*  Apologies for the cruddy way I've written this function.  It has had   *|
  184. |*  bits added onto it in a very ad-hoc way, including the #ifdef'd bits   *|
  185. |*  that determine what locking mechanism is used, and the result is,       *|
  186. |*  shall we say, sub-optimal elegance.     Particularly nasty is that way       *|
  187. |*  that when the lockf(3)ing locking mechanism is used, we maintain       *|
  188. |*  two open file-descriptors at once on the same file, but it's the       *|
  189. |*  quickest and easiest way to use the existing get_scores() function,       *|
  190. |*  and it does at least work.    Since I don't anticipate anything else       *|
  191. |*  significant being added to the function, I'm going to leave it as it   *|
  192. |*  is, and not tidy it up unless I feel *really* guilty in the morning.   *|
  193. |*                                       *|
  194. \***************************************************************************/
  195.  
  196. int update_file ()
  197. {
  198.   int i = 0, j = 0, k = 0;    /* Sundry re-usable loop-indices */
  199.   int score_fd;            /* The fd through which we write new file */
  200.   int lock_fd;            /* If LOCKF is defined, we use this as */
  201.                 /* an auxiliary fd on the SCORE_FILE, one */
  202.                 /* that stays open all the time, so we can */
  203.                 /* use lockf().     Otherwise, it is the fd */
  204.                 /* that we open to the LOCK_FILE */
  205.  
  206.   (void) umask (0000);        /* 000 octal, just to make the point */
  207.  
  208. #ifdef LOCKF
  209.   if ((lock_fd = open (SCORE_FILE, O_RDWR | O_CREAT, 0666)) == -1)
  210.     die (LE_OPEN, "Couldn't open(2) score-file for lockf()");
  211.  
  212.   while (i++ < 5) {        /* Make up to five attempts */
  213.     if (lockf (lock_fd, F_TLOCK, 0) != -1)
  214.       break;            /* If we succeed, then carry on */      
  215.                 /* Otherwise, if not due to exclusivity */
  216.     if (errno != EAGAIN) {    /* then die with a system error */
  217.       print_msg ("lockf(3) error!");
  218.       (void) get_key ();
  219.       return (-1);
  220.     }
  221.     
  222.     print_msg ("Hi-score access:");
  223.     sleep (1);            /* Back off and wait ... */
  224.     print_msg ("");        /* Then try again */
  225.   }  
  226. #else /* LOCKF */
  227.   while (i++ < 5) {        /* Make up to five attempts */
  228.     if ((lock_fd = open (LOCK_FILE, O_CREAT | O_EXCL, 0666)) != -1)
  229.       break;            /* If we succeed, then carry on */
  230.                 /* Otherwise, if not due to exclusivity */
  231.     if (errno != EEXIST) {    /* then die with a system error */
  232.       print_msg ("open(2) error!");
  233.       (void) get_key ();
  234.       return (-1);
  235.     }
  236.     
  237.     print_msg ("Hi-score access:");
  238.     sleep (1);            /* Back off and wait ... */
  239.     print_msg ("");        /* Then try again */
  240.   }  
  241. #endif /* LOCKF */
  242.  
  243.   if (i > 5)            /* If we tried 5 times unsuccessfully, */
  244.     return (-1);        /* Then give up and return -1 instead */
  245.  
  246.   get_scores ();
  247.   for (i = 0; i < no_hiscores; i++) {
  248.     if (((!strcmp (user_code, hi_scores[i].code)) &&
  249.      (game_level == hi_scores[i].game_level)) &&
  250.     ((score < hi_scores[i].score) ||
  251.      ((score == hi_scores[i].score) &&
  252.       ((no_pieces < hi_scores[i].no_pieces) ||
  253.        ((no_pieces == hi_scores[i].no_pieces) &&
  254.         (no_levels < hi_scores[i].no_levels)))))) {
  255.       i = NO_HISCORES;        /* If the same user has a better score */
  256.       break;            /* on the same level, then drop this one */
  257.     }
  258.     
  259.     if ((score > hi_scores[i].score) ||
  260.     ((score == hi_scores[i].score) &&
  261.      ((no_pieces > hi_scores[i].no_pieces) ||
  262.       ((no_pieces == hi_scores[i].no_pieces) &&
  263.        ((no_levels > hi_scores[i].no_levels) ||
  264.         ((no_levels == hi_scores[i].no_levels) &&
  265.          ((game_level >= hi_scores[i].game_level)))))))) /* Lisp :-) */
  266.       break;            /* i is the new position of the player */
  267.   }
  268.   
  269.   if (i == NO_HISCORES) {    /* If we looped off the end of the array */
  270.     (void) close (lock_fd);    /* then the score isn't good enough: */
  271. #ifndef LOCKF            /* Automagically removes advisory lockf() */
  272.     (void) unlink (LOCK_FILE);
  273. #endif /* LOCKF */
  274.     return (0);
  275.   }
  276.                 /* If there is a matching score lower down */
  277.                 /* the file, set j to it, (otherwise to i) */
  278.   for (j = NO_HISCORES-1; j >= i; j--)
  279.     if ((!strcmp (user_code, hi_scores[j].code)) &&
  280.     (game_level == hi_scores[j].game_level))
  281.       break;
  282.  
  283.                 /* No duplicate score found, so just */
  284.   if (j < i) {            /* shunt up all other scores. */
  285.     for (j = NO_HISCORES-1; j > i; j--)
  286.       l_bcopy ((char*) &hi_scores[j-1], (char*) &hi_scores[j],
  287.          sizeof (struct score_ent));
  288.     if (no_hiscores < NO_HISCORES)
  289.       no_hiscores++;
  290.   }
  291.   else {            /* j points at a duplicate score of the */
  292.     for (k = j; k > i; k--) {    /* new one, so shift bits between them */
  293.       l_bcopy ((char*) &hi_scores[k-1], (char*) &hi_scores[k],
  294.          sizeof (struct score_ent));
  295.     }
  296.   }
  297.   
  298.   (void) strcpy (hi_scores[i].name, user_name);
  299.   (void) strcpy (hi_scores[i].code, user_code);
  300.   hi_scores[i].score = score;
  301.   hi_scores[i].no_pieces = no_pieces;
  302.   hi_scores[i].no_levels = no_levels;
  303.   hi_scores[i].game_level = game_level+rand_lines+(50000-total_time)/2500;
  304.  
  305.   if ((score_fd = open (SCORE_FILE, O_WRONLY | O_CREAT, 0666)) == -1) {
  306.     perror ("open");
  307.     die (LE_OPEN, "couldn't open(2) score-file for writing");
  308.   }
  309.  
  310.   if (write (score_fd, (char*) hi_scores, no_hiscores*sizeof
  311.          (struct score_ent)) == -1) {
  312.     perror ("write");
  313.     die (LE_WRITE, "couldn't write(2) to score-file");
  314.   }
  315.  
  316.   (void) close (score_fd);
  317.   (void) close (lock_fd);
  318. #ifndef LOCKF
  319.   (void) unlink (LOCK_FILE);
  320. #endif /* LOCKF */
  321.   return (i+1);
  322. }
  323.  
  324. /***************************************************************************\
  325. |*                                       *|
  326. |*    save_options() creates a little script file named pict which sets  *|
  327. |*  the current options so that when the user executes it, these will      *|
  328. |*  remain.                                        *|
  329. |*                                       *|
  330. \***************************************************************************/
  331.  
  332. void save_options() {
  333.    FILE *optfile;
  334.    optfile=fopen("pict","w");
  335.    fwrite("#! /bin/sh\n",11,1,optfile);
  336.    fprintf(optfile,"pictetris ");
  337.    if (black_bg) fprintf (optfile, "-B ");
  338.    if (!next_box) fprintf (optfile, "-n ");
  339.    if (!sound) fprintf (optfile, "-S ");
  340.    if (rotate_backwards) fprintf (optfile, "-b ");
  341.    if (game_level!=0) fprintf (optfile,"-f%d ",game_level);
  342.    if (rand_lines!=0) fprintf (optfile,"-r%d ",rand_lines);
  343.    if (total_time2!=0) fprintf (optfile,"-l%d ",total_time2);
  344.    fclose(optfile);
  345.    chmod("pict",00751);
  346.    print_msg("Done... press any key.");
  347.    myrefresh();
  348.    get_keyboard_key();
  349. }
  350.  
  351. /***************************************************************************\
  352. |*                                       *|
  353. |*  The function get_key() reads a character from the keyboard, and       *|
  354. |*  performs some simple processing on it.  If it's an 's', it lists       *|
  355. |*  the high-score table.  Otherwise, it returns 1 for a 'q' or 'n',       *|
  356. |*  and 0 for anything else.                           *|
  357. |*                                       *|
  358. \***************************************************************************/
  359.  
  360. int get_key ()
  361. {
  362.   char ch;
  363.  
  364.    ch=toupper(get_keyboard_key());
  365.    if ((ch == 's') || (ch == 'S')) {
  366.       clear_area();
  367.       print_scores ();
  368.       myrefresh ();
  369.       get_keyboard_key();
  370.    }     
  371.  
  372.    draw_area ();
  373.    flush_keyboard();
  374.    return ((ch == 'N') || (ch == 'S') || (ch == 'Q'));
  375. }
  376.  
  377. /***************************************************************************\
  378. |*                                       *|
  379. |*  The main() function handles initialisation, gets keys and names from   *|
  380. |*  the environent, parses command-line arguments and so on.  It then       *|
  381. |*  goes into the main loop of calling play_game(), and asking if the       *|
  382. |*  player wants another game, and so on.                   *|
  383. |*                                       *|
  384. \***************************************************************************/
  385.  
  386. main (argc, argv)
  387.   int argc;
  388.   char **argv;
  389. {
  390.   int i;
  391.   char *cp;            /* Temporary pointer for getenv() */
  392.   time_t ignore_me;        /* Storage for time for random seed. */
  393.   struct passwd *pw_ptr;    /* Used with getuid() to find usercode */
  394.  
  395.   char optkey;            /* Stores the key pressed during option menu */
  396.    
  397.   (void) srandom ((int) time (&ignore_me));
  398.   (void) strcpy (prog_name, basename (argv[0]));
  399.  
  400.   if ((i = getuid ()) == -1)
  401.     die (LE_GETUID, "couldn't geteuid(2)");
  402.   if ((pw_ptr = getpwuid (i)) == NULL)
  403.     die (LE_GETPW, "couldn't get password entry");
  404.   (void) strncpy (user_code, pw_ptr->pw_gecos, CODELEN-1);
  405.   user_code[CODELEN-1] = '\0';
  406.  
  407.   if ((cp = getenv ("TTNAME")) == NULL)
  408.     if ((cp = getenv ("NAME")) == NULL)
  409.       cp = user_code;
  410.   (void) strncpy (user_name, cp, NAMELEN-1);
  411.   user_name[NAMELEN-1] = '\0';
  412.  
  413.   for (i = 1; i < argc; i++)
  414.     switch (argv[i][0]) {
  415.     case '-':
  416.       switch (argv[i][1]) {
  417.       case 's':
  418.     if (argv[i][2] != '\0') 
  419.       if (((no_shown = atoi (argv[i]+2)) < 1) ||
  420.           (no_shown > NO_HISCORES)) {
  421.         static char tmp[LINELEN]; /* To stop recursive form() */
  422.         (void) sprintf (tmp, "Number of scores must be between 1 and %d",
  423.                 NO_HISCORES);
  424.         die (LE_LEVEL, tmp);
  425.       }
  426.     
  427.      setup_curses();
  428.      setup_screen();
  429.      clear_area();
  430.      print_scores ();
  431.      myrefresh();
  432.      get_key();             /* Flag '-s': print scores, then exit. */
  433.     die (LE_OK,"");
  434.       case 'b':
  435.     rotate_backwards = 1;
  436.     break;
  437.       case 'f':
  438.     if (argv[i][2] == '\0')
  439.       goto USAGE_ERROR;
  440.     else
  441.       if (((game_level = atoi (argv[i]+2)) < -10) || (game_level > 20))
  442.         die (LE_LEVEL, "Free-falling pieces must be between -10 and 20");
  443.     break;
  444.       case 'r':
  445.     if (argv[i][2] == '\0')
  446.       goto USAGE_ERROR;
  447.      else
  448.       if (((rand_lines=atoi (argv[i]+2)) < 0) || (rand_lines > 16))
  449.         die (LE_LEVEL, "Random lines must be between 0 and 16");
  450.     break;
  451.        case 'B':
  452.      black_bg=1;
  453.      break;
  454.        case 'n':
  455.      next_box=0;
  456.      break;
  457.        case 'S':
  458.      sound=0;
  459.      break;
  460.       case 'l':
  461.     if (argv[i][2] == '\0')
  462.       goto USAGE_ERROR;
  463.      else
  464.       if (((total_time2 = atoi (argv[i]+2)) < 0) || (total_time2 > 20))
  465.         die (LE_LEVEL, "Starting speed must be between 0 and 20");
  466.     break;
  467.       
  468.       default:
  469.       goto USAGE_ERROR;
  470.       }
  471.       break;
  472.     default:
  473.     USAGE_ERROR:
  474.        die (LE_USAGE, form ("Usage: %s [ -s ] [ -S ] [ -n ] [ -b ] [ -B ] [ -f# ] [ -r# ] [ -l# ]\n\n -s: show scores\n -S: No sound (sometimes annoying)\n -n: No next-piece box :(\n -b: rotate clockwise\n -B: black background\n -f: free falling pieces\n -r: random lines\n -l: starting speed\n", prog_name));
  475.     }
  476.   
  477.   if (((int) signal (SIGHUP,    signal_end)  == BADSIG2) ||
  478.       ((int) signal (SIGINT,    signal_end)  == BADSIG2) ||
  479.       ((int) signal (SIGTERM, signal_end)  == BADSIG2))
  480.     die (LE_SIGNAL, "couldn't set up signal-handling");
  481.   
  482.    setup_curses();
  483.    draw_title();
  484.    myrefresh();
  485.    flush_keyboard();
  486.    get_keyboard_key();   
  487.    
  488.    while (1) {
  489.       draw_title();
  490.       
  491.       do {
  492.       flush_keyboard();
  493.      draw_options(black_bg,next_box,sound,
  494.               rand_lines,total_time2,game_level);
  495.      optkey=toupper(get_keyboard_key());
  496.      switch ((int) optkey) {
  497.       case 'B': black_bg=(black_bg==1) ? 0 : 1;
  498.         break;
  499.       case 'S': sound=(sound==1) ? 0 : 1;
  500.         break;
  501.       case 'N': next_box=(next_box==1) ? 0 : 1;
  502.         break;
  503.       case 'L': total_time2=(total_time2==20) ? 0 : total_time2+1;
  504.         break;
  505.       case 'F': game_level=(game_level==20) ? -10 : game_level+1;
  506.         break;
  507.       case 'R': rand_lines=(rand_lines==16) ? 0 : rand_lines+1;
  508.         break;
  509.       case 'V':
  510.         setup_screen();
  511.         clear_area();
  512.         print_scores();
  513.         myrefresh();
  514.         get_key();
  515.         draw_title();
  516.         break;
  517.       case 'K':
  518.         save_options();
  519.         break;
  520.       case 'Q': die (LE_OK, "");
  521.         break;
  522.      }
  523.       } while (optkey!='C');
  524.       
  525.       setup_screen ();
  526.       clear_board ();
  527.       draw_area();
  528.       flush_keyboard();
  529.       
  530.       while (1) {
  531.      total_time=50000-total_time2*2500;
  532.      play_game ();
  533.      
  534.      if ((i = update_file ()) > 0) {
  535.         static char tmp[LINELEN]; /* To stop recursive form() phelgming */
  536.         (void) sprintf (tmp, form("Score ranks #%d", i));
  537.         print_msg (tmp);
  538.      }
  539.      if (i < 0)
  540.        print_msg ("Save-score failed!");
  541.      if (i != 0) {
  542.         flush_keyboard();
  543.         if (get_key ())
  544.           break;
  545.      }
  546.      
  547.      print_msg ("  Again? (Y/N/Scores)  ");
  548.      flush_keyboard();
  549.      if (get_key ())
  550.        break;
  551.      
  552.       }
  553.    }
  554. }
  555.    
  556. /*-------------------------------------------------------------------------*/
  557.